﻿#include "SCStatusTcp.h"
#include "SCHeadData.h"
#include <QDateTime>
#include <QNetworkProxy>
#include <memory>
#include "../global_logger.h"

SCStatusTcp::SCStatusTcp(QObject *parent) : QObject(parent),
    _tcpSocket(nullptr)
{
    LOG_INFO("SCStatusTcp对象初始化完成");
}

SCStatusTcp::~SCStatusTcp()
{
    LOG_DEBUG("销毁SCStatusTcp对象");
    releaseTcpSocket();
}

/**
 * @brief 释放TCP套接字资源
 * 
 * 该函数负责安全地释放TCP套接字对象，包括关闭连接、终止操作并释放内存。
 * 函数会检查套接字是否存在，如果存在则执行相应的清理操作。
 * 
 * @note 该函数不接受任何参数
 * @note 该函数没有返回值
 */
void SCStatusTcp::releaseTcpSocket()
{
    // 如果TCP套接字对象存在，则进行资源释放操作
    if (_tcpSocket) {
        LOG_DEBUG("释放TCP套接字资源 - 当前状态: {}", 
                            static_cast<int>(_tcpSocket->state()));
        
        // 如果套接字处于打开状态，则先关闭连接
        if (_tcpSocket->isOpen()) {
            _tcpSocket->close();
            LOG_DEBUG("TCP套接字已关闭");
        }
        // 终止套接字的所有操作
        _tcpSocket->abort();
        // 释放套接字对象的内存
        delete _tcpSocket;
        // 将套接字指针置为空，避免野指针
        _tcpSocket = nullptr;
        LOG_DEBUG("TCP套接字资源已释放");
    }
}

/**
 * @brief 连接到指定的主机IP和端口
 * @param ip 目标主机的IP地址
 * @param port 目标主机的端口号
 * @return 连接结果状态码：0-连接成功或正在进行，1-已有连接被关闭
 * 
 * 该函数负责建立TCP连接，如果已存在连接则先关闭，
 * 然后创建新的QTcpSocket对象并连接到指定主机。
 */
int SCStatusTcp::connectHost(const QString &ip, quint16 port)
{
    LOG_INFO("尝试连接到主机 - IP: {}, 端口: {}", ip.toStdString(), port);
    
    // 检查TCP套接字是否存在，不存在则创建并初始化
    if (!_tcpSocket) {
        _tcpSocket = new QTcpSocket(this);
        _tcpSocket->setProxy(QNetworkProxy::NoProxy);
        connect(_tcpSocket, &QTcpSocket::readyRead, this, &SCStatusTcp::receiveTcpReadyRead);
        LOG_DEBUG("创建新的QTcpSocket对象");
    }

    // 检查当前连接状态，如果已连接或正在连接，则关闭现有连接
    if (_tcpSocket->isOpen() &&
        (_tcpSocket->state() == QAbstractSocket::ConnectedState ||
         _tcpSocket->state() == QAbstractSocket::ConnectingState)) {
        _tcpSocket->close();
        _tcpSocket->abort();
        LOG_INFO("关闭现有连接以建立新连接");
        return 1;
    }

    // 建立新的TCP连接（非阻塞方式）
    _tcpSocket->connectToHost(ip, port, QTcpSocket::ReadWrite, QTcpSocket::IPv4Protocol);
    _ip = ip;
    _port = port;
    LOG_DEBUG("已发起连接请求 - IP: {}, 端口: {}", ip.toStdString(), port);

    // 清空可能存在的旧数据
    if (_tcpSocket->isOpen())
        _tcpSocket->readAll();

    return 0;
}

/**
 * @brief 向TCP套接字写入数据，构造包含命令和数据的协议包并发送。
 *
 * 该函数用于将指定的命令、JSON数据和附加数据打包成协议格式，并通过TCP套接字发送出去。
 * 数据包的格式由 SeerData（包含 SeerHeader）定义。函数会检查TCP连接状态，
 * 并在发送前确保套接字处于连接状态。
 *
 * @param sendCommand 要发送的命令码（uint16_t）
 * @param jsonData    要发送的JSON格式数据（QByteArray）
 * @param sendData    要发送的附加二进制数据（QByteArray）
 * @param number      一个用于协议的编号字段，可能用于标识或序列号（uint16_t 引用）
 * @param byte15      协议中第15字节的控制字段（uint8_t）
 *
 * @return 成功发送返回 true，否则返回 false。错误信息保存在 _lastError 中。
 */
bool SCStatusTcp::writeTcpData(uint16_t sendCommand,
                               const QByteArray &jsonData,
                               const QByteArray &sendData,
                               uint16_t &number,
                               uint8_t byte15)
{
    // 检查TCP套接字是否已初始化
    if (!_tcpSocket) {
        _lastError = tr("TCP socket is not initialized!");
        LOG_ERROR("TCP套接字未初始化");
        return false;
    }

    // 检查TCP套接字是否处于连接状态
    if (_tcpSocket->state() != QAbstractSocket::ConnectedState) {
        _lastError = tr("TCP socket is not connected!");
        LOG_WARN("TCP套接字未连接 - 当前状态: {}", static_cast<int>(_tcpSocket->state()));
        return false;
    }

    // 记录当前发送的命令和编号，用于后续处理
    _oldSendCommand = sendCommand;
    _oldNumber = number;

    int size = 0;
    std::unique_ptr<uint8_t[]> headBuf;
    SeerData* seerData = nullptr;

    // 启动计时器，用于统计发送耗时
    _time.start();

    // 计算JSON数据长度，若为空则为0
    uint16_t jsonSize = jsonData.isEmpty() ? 0 : static_cast<uint16_t>(jsonData.toStdString().length());

    // 拼接JSON数据和附加数据作为整体发送数据
    QByteArray totalData = jsonData + sendData;

    // 如果没有数据需要发送，则构造一个空数据包
    if (totalData.isEmpty()) {
        int headSize = sizeof(SeerHeader);
        headBuf.reset(new uint8_t[headSize]);
        seerData = reinterpret_cast<SeerData*>(headBuf.get());
        size = seerData->setData(sendCommand, nullptr, 0, 0, number, byte15);
    } else {
        // 若有数据，则构造包含数据的完整协议包
        std::string totalDataStr = totalData.toStdString();
        int headSize = sizeof(SeerHeader) + totalDataStr.length();
        headBuf.reset(new uint8_t[headSize]);
        seerData = reinterpret_cast<SeerData*>(headBuf.get());
        size = seerData->setData(sendCommand,
                                 reinterpret_cast<const uint8_t*>(totalDataStr.data()),
                                 totalDataStr.length(),
                                 jsonSize,
                                 number, byte15);
    }

    // 打印即将发送的数据包的十六进制内容，便于调试
    QByteArray tempA = QByteArray::fromRawData(reinterpret_cast<char*>(seerData), size);
    QString hexStr = formatHex(tempA);
    //LOG_DEBUG("---> 发送数据 Hex:{}  size::{}", hexStr.toStdString(), size);
    // 打印附加数据的十六进制表示，若数据过长则不打印
    //QString dataHex = (size <= 2048) ? hexToQString(sendData.toHex()) : tr("If the data region length is larger than 2048 bytes, it will not be printed");
    
    // 再次确认TCP连接状态，防止在构造数据包过程中连接断开
    if (_tcpSocket->state() != QAbstractSocket::ConnectedState) {
        _lastError = tr("TCP socket disconnected before write!");
        LOG_WARN("在发送数据前TCP套接字断开连接");
        return false;
    }

    // 执行实际的数据发送操作
    qint64 bytesWritten = _tcpSocket->write(reinterpret_cast<char*>(seerData), size);
    if (bytesWritten == -1) {
        // 发送失败，记录错误信息
        _lastError = tr("Failed to write data to socket: %1").arg(_tcpSocket->errorString());
        LOG_ERROR("发送数据失败: {}", _tcpSocket->errorString().toStdString());
        return false;
    }

    //LOG_DEBUG("数据发送成功 - 命令: {}, 数据包大小: {} 字节, 实际写入: {} 字节", sendCommand, size, bytesWritten);
    
    // 发送成功
    return true;
}

/**
 * @brief 异步写入TCP数据
 * 
 * 该函数用于异步发送TCP数据包，首先将请求ID与命令码建立映射关系，
 * 然后调用底层写入函数发送数据。
 * 
 * @param cmd 命令码，标识数据包的类型
 * @param data 要发送的数据内容
 * @param reqId 请求ID，用于标识和匹配请求响应
 */
void SCStatusTcp::asyncWriteTcpData(uint16_t cmd, const QByteArray &data, uint16_t reqId)
{
    //LOG_WARN("异步发送TCP数据 - 命令: {}, 请求ID: {}, 数据大小: {}", cmd, reqId, data.size());
    
    // 将请求ID与命令码建立映射关系，用于后续响应匹配
    m_reqIdCmdMap[reqId] = cmd;
    // 调用底层TCP数据写入函数发送数据包
    writeTcpData(cmd, data, QByteArray(), reqId, 0);
}

/**
 * @brief 处理TCP套接字接收到数据时的槽函数
 *
 * 该函数在TCP套接字有可读数据时被调用，负责读取并解析接收到的数据。
 * 数据格式包含一个16字节的头部（SeerHeader）和后续的数据体。
 * 根据头部信息判断数据是否完整，并提取JSON数据和二进制数据部分进行处理。
 * 若数据不完整则缓存到_lastMessage中，等待下一次接收继续拼接处理。
 *
 * 主要功能包括：
 * - 读取TCP数据
 * - 解析协议头部
 * - 检查数据完整性
 * - 提取JSON和二进制数据
 * - 发送解析后的信号
 *
 * @note 该函数假设使用大端字节序传输数据
 */
void SCStatusTcp::receiveTcpReadyRead()
{    
    // 读取当前所有可用数据，并与上次未处理完的数据拼接
    QByteArray message = _tcpSocket->readAll();
    message = _lastMessage + message;
    int size = message.size();
    //LOG_WARN("读取到数据 - 大小: {} 字节, 缓存数据大小: {} 字节", message.size(), _lastMessage.size());
    
    int processedCount = 0;
    
    // 循环处理接收到的数据包
    while (size > 0) {
        // 判断消息头是否为0x5A（协议起始标志）
        if (static_cast<uint8_t>(message.at(0)) == 0x5A) {
            // 至少需要16字节才能解析头部
            if (size >= 16) {
                // 解析头部信息
                std::unique_ptr<SeerHeader> header(new SeerHeader);
                memcpy(header.get(), message.data(), 16);

                // 将网络字节序转换为主机字节序
                uint32_t data_size;
                uint16_t revCommand;
                uint16_t number;
                qToBigEndian(header->m_length, reinterpret_cast<uint8_t*>(&data_size));
                qToBigEndian(header->m_type, reinterpret_cast<uint8_t*>(&revCommand));
                qToBigEndian(header->m_number, reinterpret_cast<uint8_t*>(&number));
                // 修改JSON数据长度提取逻辑，使用更多字节或重新设计协议
                uint32_t jsonSize = 0;
                {
                    // 使用4个字节存储JSON长度（如果协议允许）
                    uint8_t u0 = header->m_reserved[0];  // 假设扩展使用更多字节
                    uint8_t u1 = header->m_reserved[1];
                    uint8_t u2 = header->m_reserved[2];
                    uint8_t u3 = header->m_reserved[3];
                    uint32_t tempJsonSize = (u0 << 24) | (u1 << 16) | (u2 << 8) | u3;
                    if (tempJsonSize <= data_size)  // 确保不超过总数据大小
                        jsonSize = tempJsonSize;
                }

                // 计算剩余数据大小
                uint32_t remaining_size = size - 16;
                /*LOG_WARN("解析数据包头部 - 数据大小: {}, 命令: {}, 编号: {}, JSON大小: {}", 
                                    data_size, revCommand, number, jsonSize);*/
                
                // 如果数据不完整，保存到_lastMessage等待下次处理
                if (data_size > remaining_size) {
                    _lastMessage = message;
                    break;
                } else {
                    // 构造完整消息用于调试输出
                    QByteArray tempMessage = _lastMessage.isEmpty() ? message : _lastMessage;

                    // 分离头部、JSON数据和二进制数据
                    QByteArray headB = message.left(16);
                    QByteArray jsonData;
                    QByteArray byteData;
                    {
                        QByteArray totalData = message.mid(16, data_size);
                        if (jsonSize > 0) {
                            jsonData = totalData.mid(0, jsonSize);
                            //qDebug() << __func__ << " <=== jsonSize:" << jsonSize;
                            //qDebug() << "---> tempJsonData:" << QString("[%1]").arg(QString(jsonData));
                            //LOG_DEBUG("<--- 解析数据包 - 头部: {}, jsonSize:{}, JSON数据: {}", headB.toHex(), jsonSize, jsonData.toStdString());
                        }
                        byteData = totalData.mid(jsonSize, data_size - jsonSize);
                        //qDebug() << "jsonData:" << QString(jsonData) << "revByteData:" << QString(byteData) << "  Hex:" << byteData.toHex();
                    }
                    // 构造十六进制字符串用于显示（超过2048字节不显示）
                    //QString dataHex = (size <= 2048) ? hexToQString(byteData.toHex()) : tr("If the data region length is larger than 2048 bytes, it will not be printed");
                    //int msTime = _time.elapsed();

                    // 发送文本变化信号
                    /*emit sigChangedText(true, revCommand,
                                        byteData, byteData.toHex(),
                                        number, msTime);*/

                    // 根据是否有JSON数据发送不同的响应信号
                    if (jsonSize > 0) {
                        emit responseReceived(revCommand, number, jsonData);
                        //LOG_WARN("发送响应信号 - 带JSON数据, 命令: {}, 编号: {}, JSON大小: {}", revCommand, number, jsonData.size());
                    } else {
                        emit responseReceived(revCommand, number, byteData);
                        //LOG_WARN("发送响应信号 - 二进制数据, 命令: {}, 编号: {}, 数据大小: {}", revCommand, number, byteData.size());
                    }

                    // 更新剩余未处理数据
                    message = message.right(remaining_size - data_size);
                    size = message.size();
                    _lastMessage.clear();
                    processedCount++;
                }
            } else {
                // 头部不足16字节，缓存等待更多数据
                _lastMessage = message;
                //LOG_WARN("头部数据不足16字节，缓存等待更多数据 - 当前大小: {}", size);
                break;
            }
        } else {
            // 非法头部，记录错误并跳过第一个字节
            setLastError("Header Error !!!");
            LOG_ERROR("检测到非法数据包头部");
            message = message.right(size - 1);
            size = message.size();
            /*int msTime = _time.elapsed();
            emit sigChangedText(false, _oldSendCommand,
                                _lastMessage, _lastMessage.toHex(),
                                0, msTime);*/
        }
    }
}

/**
 * @brief 获取超时时间
 * @return 返回当前设置的超时时间值
 */
int SCStatusTcp::getTimeOut() const
{
    return _timeOut;
}

/**
 * @brief 设置超时时间
 * @param timeOut 要设置的超时时间值
 */
void SCStatusTcp::setTimeOut(int timeOut)
{
    _timeOut = timeOut;
    LOG_WARN("设置超时时间: {}ms", timeOut);
}

/**
 * @brief 获取TCP套接字对象
 * @return 返回QTcpSocket类型的指针
 */
QTcpSocket *SCStatusTcp::tcpSocket() const
{
    return _tcpSocket;
}

/**
 * @brief 获取计时器对象
 * @return 返回QElapsedTimer类型的计时器对象
 */
QElapsedTimer SCStatusTcp::time() const
{
    return _time;
}

/**
 * @brief 设置最后错误信息
 * @param lastError 要设置的错误信息字符串
 */
void SCStatusTcp::setLastError(const QString &lastError)
{
    _lastError = lastError;
    LOG_DEBUG("设置最后错误信息: {}", lastError.toStdString());
}

/**
 * @brief 获取最后错误信息
 * @return 返回存储的最后错误信息字符串
 */
QString SCStatusTcp::lastError() const
{
    return _lastError;
}

/**
 * @brief 获取当前日期时间格式化字符串
 * @return 返回格式化后的当前日期时间字符串，格式为[yyyyMMdd|hh:mm:ss:zzz]:
 */
QString SCStatusTcp::getCurrentDateTime() const
{
    return QDateTime::currentDateTime().toString("[yyyyMMdd|hh:mm:ss:zzz]:");
}

/**
 * @brief 将QByteArray中的字节数据转换为QString格式
 * @param b 输入的QByteArray数据
 * @return 转换后的QString字符串，包含所有字节的数值表示，以大写形式返回
 */
QString SCStatusTcp::hexToQString(const QByteArray &b)
{
    QString str;
    // 遍历字节数组中的每个字节，将其转换为字符串并拼接
    for (int i = 0; i < b.size(); ++i) {
        str += QString("%1").arg(b.at(i));
    }
    return str.toUpper();
}

/**
 * @brief 处理TCP套接字状态变化的槽函数
 * @param state 当前套接字的状态
 */
void SCStatusTcp::stateChanged(QAbstractSocket::SocketState state)
{
    // 输出套接字状态变化信息到调试日志
    LOG_DEBUG("套接字状态变化: {}", static_cast<int>(state));
}

/**
 * @brief 处理TCP套接字错误的槽函数
 * @param error 发生的套接字错误类型
 */
void SCStatusTcp::receiveTcpError(QAbstractSocket::SocketError error)
{
    // 输出套接字错误信息到调试日志
    LOG_ERROR("套接字错误: {}", static_cast<int>(error));
}

/**
 * @brief 将QByteArray数据格式化为十六进制字符串显示
 * @param data 输入的QByteArray数据
 * @return 格式化后的十六进制字符串，每个字节用两位十六进制数表示，字节间用空格分隔，以大写形式返回
 */
QString SCStatusTcp::formatHex(const QByteArray &data) const
{
    QString hex;
    // 遍历数据中的每个字节，转换为两位十六进制数并添加空格分隔
    for (int i = 0; i < data.size(); ++i) {
        hex += QString("%1 ").arg(static_cast<uint8_t>(data[i]), 2, 16, QChar('0')).toUpper();
    }
    // 移除末尾多余的空格
    if (!hex.isEmpty()) {
        hex.chop(1);
    }
    return hex;
}